import { useState, useEffect } from "react"; import { StyleSheet, View, ScrollView, Pressable, ActivityIndicator, } from "react-native"; import { Text } from "@/components/ui/Text"; import { useLocalSearchParams, router, Stack } from "expo-router"; import { MaterialIcons, Ionicons } from "@expo/vector-icons"; import { Card, SubtleInfoCard } from "@/components/ui/Cards"; import { Button } from "@/components/ui/Button"; import { Col, Row } from "@/components/ui/LayoutHelpers"; import { Section, SectionHeader } from "@/components/ui/Sections"; import { Badge } from "@/components/ui/Badge"; import { Typography, Theme, Colors, Spacing } from "@/constants/theme"; import { useCareAction, useDeleteCareAction, } from "@/hooks/useCareAction.queries"; import { useTrigger } from "@/hooks/useTrigger.queries"; import { useMoodEntry } from "@/hooks/useMoodEntries.queries"; import { getActivityCategory } from "@/constants/care-action-categories"; import { getTriggerCategory } from "@/constants/trigger-categories"; import { getMood } from "@/constants/moods"; import { formatMoment } from "@/lib/utils/time"; import type { CareAction } from "@/lib/api"; // TODO: Generalize these into a single source of truth, similar to other // constants // label helpers const APPOINTMENT_LABELS: Record = { ANALYST: "Analista", PSYCHIATRIST: "Psiquiatra", GP: "Clínico geral", NUTRITIONIST: "Nutricionista", PHYSIOTHERAPIST: "Fisioterapeuta", OTHER: "Consulta", }; const ACTIVITY_LABELS: Record = { WALK: "Caminhada", YOGA: "Yoga", GYM: "Academia", MEDITATION: "Meditação", SOCIAL: "Momento social", CREATIVE: "Atividade criativa", OTHER: "Atividade", }; const PERIODICITY_LABELS: Record = { ONCE: "Dose única", DAILY: "1× ao dia", TWICE_DAILY: "2× ao dia", THREE_TIMES_DAILY: "3× ao dia", WEEKLY: "1× por semana", BIWEEKLY: "2× por semana", MONTHLY: "1× por mês", }; // header meta type HeaderMeta = { icon: string; color: string; iconBg: string; title: string; }; function resolveHeader(ca: CareAction): HeaderMeta { switch (ca.type) { case "MEDICINE": return { icon: "medication", color: "#7C3AED", iconBg: "#EDE9FE", title: ca.medicineLog?.regimen?.name ?? "Medicamento", }; case "APPOINTMENT": return { icon: "psychology", color: "#1D4ED8", iconBg: "#DBEAFE", title: `Consulta · ${APPOINTMENT_LABELS[ca.appointment?.type ?? ""] ?? "Consulta"}`, }; case "ACTIVITY": { const meta = getActivityCategory(ca.activity?.type); return { icon: meta.icon, color: meta.color, iconBg: meta.iconBackground, title: ACTIVITY_LABELS[ca.activity?.type ?? ""] ?? "Atividade", }; } default: return { icon: "star", color: "#6B7280", iconBg: "#F3F4F6", title: "Ação de cuidado", }; } } // DetailRow // TODO: Might as well generalize this into a component type DetailRowProps = { icon: string; label: string; value: string; }; function DetailRow({ icon, label, value }: DetailRowProps) { return ( {label} {value} ); } export default function CareActionDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const { data: careAction, isLoading, isError } = useCareAction(id); const deleteCareAction = useDeleteCareAction(); const triggerId = String(careAction?.triggerId ?? ""); const moodId = String(careAction?.moodId ?? ""); const { data: trigger } = useTrigger(triggerId); const { data: mood } = useMoodEntry(moodId); const [minutesLeft, setMinutesLeft] = useState(0); const [canDelete, setCanDelete] = useState(false); useEffect(() => { const updateTimer = () => { const createdAt = careAction?.createdAt; if (!createdAt) return; const msSinceCreation = Date.now() - new Date(createdAt).getTime(); const windowMs = 5 * 60 * 1000; setCanDelete(msSinceCreation < windowMs); setMinutesLeft( Math.max(0, Math.ceil((windowMs - msSinceCreation) / 60000)), ); }; if (isLoading) return; updateTimer(); const interval = setInterval(updateTimer, 1000); return () => clearInterval(interval); }, [careAction, isLoading]); if (isLoading) { return ( ); } if (isError || !careAction) { return ( Registro não encontrado. router.back()} style={styles.backButton}> Voltar ); } const header = resolveHeader(careAction); // TODO: Componentize this as well? It's on each of the details screens const deleteButtonMsg = () => { const suffix = canDelete ? `(Disp. por ${minutesLeft < 1 ? "<1" : minutesLeft}:00)` : "(Expirado)"; return `Excluir Registro ${suffix}`; }; const handleDelete = async () => { await deleteCareAction.mutateAsync(String(careAction.id)); router.back(); }; const hasLink = !!(careAction.triggerId || careAction.moodId); return ( <> {header.title} {formatMoment(new Date(careAction.moment))} {careAction.type === "MEDICINE" && ( <> {careAction.medicineLog?.regimen?.dosage ? ( ) : null} {careAction.medicineLog?.regimen?.periodicity ? ( ) : null} )} {careAction.type === "APPOINTMENT" && ( <> {careAction.appointment?.duration ? ( ) : null} )} {careAction.type === "ACTIVITY" && ( <> {careAction.activity?.duration ? ( ) : null} )} {careAction.appointment?.note ? (
"{careAction.appointment.note.trim()}"
) : null} {hasLink && (
{trigger && ( router.push(`/triggers/${trigger.id}`) } style={({ pressed }) => [pressed && { opacity: 0.7 }]} > {(() => { const meta = getTriggerCategory(trigger.category); return ( ); })()} {getTriggerCategory(trigger.category).label} {formatMoment(new Date(trigger.moment))} )} {mood && ( router.push(`/entry/${mood.id}`) } style={({ pressed }) => [pressed && { opacity: 0.7 }]} > {getMood(mood.selectedMood.toLowerCase())?.icon ?? "😐"} {getMood(mood.selectedMood.toLowerCase())?.label ?? mood.selectedMood} {formatMoment(new Date(mood.moment))} )}
)} GESTÃO DO REGISTRO